﻿// ColorMath class.  Copyright (C) 1999-2014 Nathaniel Moschkin.
// This is the 4th writing of this class.

// 1st adaptation to C#.

using DataTools;
using System;
using System.Runtime.InteropServices;

namespace DataTools.Math
{
    /// <summary>
    /// Represents a 32-bit ARGB color value.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct Color
    {
        private uint value;
                
        /// <summary>
        /// Represents transparent black.
        /// </summary>
        public static readonly Color Transparent = new Color(0);
        
        /// <summary>
        /// Represents an empty color structure.
        /// </summary>
        public static readonly Color Empty = new Color(0);
        
        /// <summary>
        /// Returns the absolute color value as an unsigned 32-bit integer.
        /// </summary>
        public uint Value
        {
            get
            {
                return this.value;
            }
            set
            {
                this.value = value;
            }
        }

        /// <summary>
        /// Alpha channel.
        /// </summary>
        public byte A
        {
            get
            {
                return (byte)((Value >> 24) & 0xff);
            }
            set
            {
                uint x = this.value & 0x00ffffff;
                uint a = (uint)value;

                x = (a << 24) | x;
                this.value = x;
            }
        }

        /// <summary>
        /// Red channel.
        /// </summary>
        public byte R
        {
            get
            {
                return (byte)((Value >> 16) & 0xff);
            }
            set
            {
                uint x = this.value & 0xff00ffff;
                uint a = (uint)value;

                x = (a << 16) | x;
                this.value = x;
            }
        }

        /// <summary>
        /// Green channel.
        /// </summary>
        public byte G
        {
            get
            {
                return (byte)((Value >> 8) & 0xff);
            }
            set
            {
                uint x = this.value & 0xffff00ff;
                uint a = (uint)value;

                x = (a << 8) | x;
                this.value = x;
            }
        }

        /// <summary>
        /// Blue channel.
        /// </summary>
        public byte B
        {
            get
            {
                return (byte)(Value & 0xff);
            }
            set
            {
                uint x = this.value & 0xffffff00;
                uint a = (uint)value;

                x = a | x;
                this.value = x;
            }
        }

        /// <summary>
        /// Returns a signed 32-bit integer representation of this color value.
        /// </summary>
        /// <returns></returns>
        public int ToArgb()
        {
            int x = (int)A;
            x <<= 8;

            x |= (int)R;
            x <<= 8;

            x |= (int)G;
            x <<= 8;

            x |= (int)B;

            return x;
        }

        /// <summary>
        /// Returns
        /// </summary>
        /// <param name="a">Alpha channel.</param>
        /// <param name="r">Red channel.</param>
        /// <param name="g">Green channel.</param>
        /// <param name="b">Blue channel.</param>
        /// <returns></returns>
        public static Color FromArgb(int a, int r, int g, int b)
        {
            return new Color(a, r, g, b);
        }

        /// <summary>
        /// Initialize a new Color structure the specified alpha, red, green and blue channel values.
        /// </summary>
        /// <param name="a">Alpha channel.</param>
        /// <param name="r">Red channel.</param>
        /// <param name="g">Green channel.</param>
        /// <param name="b">Blue channel.</param>
        public Color(int a, int r, int g, int b)
        {
            value = (uint)a;
            value <<= 8;

            value |= (uint)r;
            value <<= 8;

            value |= (uint)g;
            value <<= 8;

            value |= (uint)b;
        }

        /// <summary>
        /// Initialize a new Color structure with the specified signed 32-bit color value.
        /// </summary>
        /// <param name="color">A 32-bit signed color value.</param>
        public Color(int color)
        {
            uint a = 0xff & (uint)(color >> 24);
            color = (color & 0xffffff);

            value = (uint)color;
            value |= (a << 24);
        }

        /// <summary>
        /// Initialize a new Color structure with the specified unsigned 32-bit color value.
        /// </summary>
        /// <param name="color">A 32-bit unsigned color value.</param>
        public Color(uint color)
        {
            value = color;
        }

        /// <summary>
        /// Initialize a new Color structure from the specified '#xxxxxx' color string.
        /// </summary>
        /// <param name="color"></param>
        public Color(string color)
        {
            value = ColorMath.UnCex(color).value;
        }

        /// <summary>
        /// Returns the hash-code of the current structure.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return ToArgb();
        }

        /// <summary>
        /// Compares this structure to another color structure;
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (obj.GetType() != typeof(Color)) return false;
            return ((Color)(obj)).ToArgb() == ToArgb();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public static bool operator ==(Color value1, Color value2)
        {
            return (value1.value == value2.value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value1"></param>
        /// <param name="value2"></param>
        /// <returns></returns>
        public static bool operator !=(Color value1, Color value2)
        {
            return (value1.value != value2.value);
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator uint(Color value)
        {
            return value.Value;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator Color(uint value)
        {
            return new Color(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator int(Color value)
        {
            return value.ToArgb();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator Color(int value)
        {
            return new Color(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator byte[](Color value)
        {
            byte[] b = new byte[4];

            b[0] = value.B;
            b[1] = value.G;
            b[2] = value.R;
            b[3] = value.A;

            return b;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator Color(byte[] value)
        {
            return new Color(BitConverter.ToInt32(value, 0));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static explicit operator string(Color value)
        {
            return value.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static explicit operator Color(string value)
        {
            return ColorMath.UnCex(value);
        }
        
        /// <summary>
        /// Returns a hexadecimal string representation of a color.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return ColorMath.Cex(this);
        }

    }

    /// <summary>
    /// Color calculation utility class.
    /// </summary>
    public static class ColorMath
    {
        /// <summary>
        /// Represents Red, Green, Blue color byte data.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct RGBDATA
        {
            /// <summary>
            /// Blue
            /// </summary>
            public byte Blue;

            /// <summary>
            /// Green
            /// </summary>
            public byte Green;

            /// <summary>
            /// Red
            /// </summary>
            public byte Red;
        }

        /// <summary>
        /// Represents Alpha, Red, Green, Blue color byte data.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct ARGBDATA
        {
            /// <summary>
            /// Blue
            /// </summary>
            public byte Blue;

            /// <summary>
            /// Green
            /// </summary>
            public byte Green;

            /// <summary>
            /// Red
            /// </summary>
            public byte Red;

            /// <summary>
            /// Alpha
            /// </summary>
            public byte Alpha;
        }

        /// <summary>
        /// Represents Blue, Green, Red, Alpha color byte data.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct BGRADATA
        {
            /// <summary>
            /// Alpha
            /// </summary>
            public byte Alpha;

            /// <summary>
            /// Red
            /// </summary>
            public byte Red;

            /// <summary>
            /// Green
            /// </summary>
            public byte Green;

            /// <summary>
            /// Blue
            /// </summary>
            public byte Blue;
        }

        /// <summary>
        /// Represents Blue, Green, Red color byte data.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct BGRDATA
        {
            /// <summary>
            /// Red
            /// </summary>
            public byte Red;

            /// <summary>
            /// Green
            /// </summary>
            public byte Green;

            /// <summary>
            /// Blue
            /// </summary>
            public byte Blue;
        }

        /// <summary>
        /// Represents Cyan, Magenta, Yellow color byte data.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct CMYDATA
        {
            /// <summary>
            /// Cyan
            /// </summary>
            public byte Cyan;

            /// <summary>
            /// Magenta
            /// </summary>
            public byte Magenta;

            /// <summary>
            /// Yellow
            /// </summary>
            public byte Yellow;
        }

        /// <summary>
        /// Represents Hue, Saturation, Value data.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct HSVDATA
        {
            /// <summary>
            /// Hue
            /// </summary>
            public double Hue;

            /// <summary>
            /// Saturation
            /// </summary>
            public double Saturation;

            /// <summary>
            /// Value
            /// </summary>
            public double Value;
            
            /// <summary>
            /// 
            /// </summary>
            /// <param name="value"></param>
            /// <returns></returns>
            public static explicit operator Color(HSVDATA value)
            {
                return ColorMath.HSVToColor(value);
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="value"></param>
            /// <returns></returns>
            public static explicit operator HSVDATA(Color value)
            {
                HSVDATA d = new HSVDATA();
                ColorMath.ColorToHSV(value, ref d);
                return d;
            }
        
        }

        /// <summary>
        /// Represents Hue, Saturation, Lightness data.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct HSLDATA
        {
            /// <summary>
            /// Hue
            /// </summary>
            public double Hue;

            /// <summary>
            /// Saturation
            /// </summary>
            public double Saturation;

            /// <summary>
            /// Lightness
            /// </summary>
            public double Lightness;
        }

        /// <summary>
        /// Converts a color into a '#xxxxxx' color string.
        /// </summary>
        /// <param name="Color">The color to convert.</param>
        /// <returns>A color string.</returns>
        public static string Cex(Color Color)
        {
            string s = "";
            byte[] b = BitConverter.GetBytes(Color.ToArgb());

            for (int i = 3; i >= 0; i--)
            {
                s += b[i].ToString("X2");
            }

            return "#" + s.ToLower();
        }

        /// <summary>
        /// Returns a color from a '#xxxxxx' color string.
        /// </summary>
        /// <param name="value">The string to parse.</param>
        /// <returns>A Color structure.</returns>
        public static Color UnCex(string value)
        {
            string[] s = null;
            s = new string[4];

            if (value.Substring(0, 1) != "#")
                return Color.Transparent;

            value = value.Substring(1);

            if (value.Length == 2)
            {
                s[0] = value.Substring(0, 2);
                s[1] = value.Substring(0, 2);
                s[2] = value.Substring(0, 2);
            }
            else if (value.Length == 6)
            {
                s[0] = value.Substring(0, 2);
                s[1] = value.Substring(2, 2);
                s[2] = value.Substring(4, 2);
            }
            else if (value.Length == 8)
            {
                s[0] = value.Substring(2, 2);
                s[1] = value.Substring(4, 2);
                s[2] = value.Substring(6, 2);
                s[3] = value.Substring(0, 2);
            }

            int a = 255, r, g, b;

            int.TryParse(s[0], System.Globalization.NumberStyles.AllowHexSpecifier, System.Globalization.CultureInfo.CurrentCulture, out r);
            int.TryParse(s[1], System.Globalization.NumberStyles.AllowHexSpecifier, System.Globalization.CultureInfo.CurrentCulture, out g);
            int.TryParse(s[2], System.Globalization.NumberStyles.AllowHexSpecifier, System.Globalization.CultureInfo.CurrentCulture, out b);

            if (value.Length == 8)
            {
                int.TryParse(s[3], System.Globalization.NumberStyles.AllowHexSpecifier, System.Globalization.CultureInfo.CurrentCulture, out a);
            }
           
            return Color.FromArgb(a, r, g, b);
        }

        /// <summary>
        /// Returns the red, green and blue values from a color.
        /// </summary>
        /// <param name="Color">The color to split.</param>
        /// <param name="r">Red</param>
        /// <param name="g">Green</param>
        /// <param name="b">Blue</param>
        public static void GetRGB(Color Color, ref int r, ref int g, ref int b)
        {
            int x = Color.ToArgb();

            b = (x & 0xff);
            g = (x >> 8) & 0xff;
            r = (x >> 16) & 0xff;
        }

        /// <summary>
        /// Returns the red, green and blue values from a color.
        /// </summary>
        /// <param name="Color">The color to split.</param>
        /// <param name="r">Red</param>
        /// <param name="g">Green</param>
        /// <param name="b">Blue</param>
        public static void GetRGB(Color Color, ref byte r, ref byte g, ref byte b)
        {
            int x = Color.ToArgb();

            b = (byte)(x & 0xff);
            g = (byte)((x >> 8) & 0xff);
            r = (byte)((x >> 16) & 0xff);
        }

        // Single Convert ColorRef to RGB

        /// <summary>
        /// Converts a color to RGB data.
        /// </summary>
        /// <param name="Color">The color to convert.</param>
        /// <param name="Bits">The RGBDaTA structure to fill.</param>
        public static void ColorToRGB(Color Color, ref RGBDATA Bits)
        {
            byte[] b = null;
            b = BitConverter.GetBytes(Color.ToArgb());

            Bits.Red = b[2];
            Bits.Green = b[1];
            Bits.Blue = b[0];
        }

        /// <summary>
        /// Converts a color to ARGB data.
        /// </summary>
        /// <param name="Color">The color to convert.</param>
        /// <param name="Bits">The ARGBDATA structure to fill.</param>
        public static void ColorToARGB(Color Color, ref ARGBDATA Bits)
        {
            byte[] b = null;
            b = BitConverter.GetBytes(Color.ToArgb());

            Bits.Alpha = b[3];
            Bits.Red = b[2];
            Bits.Green = b[1];
            Bits.Blue = b[0];
        }

        /// <summary>
        /// Converts RGB data to a color
        /// </summary>
        /// <param name="Bits"></param>
        /// <returns></returns>
        public static Color RGBToColor(RGBDATA Bits)
        {
            return Color.FromArgb(255, Bits.Red, Bits.Green, Bits.Blue);
        }

        /// <summary>
        /// Converts ARGB data to a color.
        /// </summary>
        /// <param name="Bits"></param>
        /// <returns></returns>
        public static Color ARGBToColor(ARGBDATA Bits)
        {
            return Color.FromArgb(Bits.Alpha, Bits.Red, Bits.Green, Bits.Blue);
        }

        /// <summary>
        /// Convert a color to BGR data.
        /// </summary>
        /// <param name="Color"></param>
        /// <param name="Bits"></param>
        public static void ColorToBGR(Color Color, ref BGRDATA Bits)
        {
            RGBDATA tibs = new RGBDATA();

            ColorToRGB(Color, ref tibs);

            Bits.Blue = tibs.Blue;
            Bits.Red = tibs.Red;
            Bits.Green = tibs.Green;
        }

        /// <summary>
        /// Converts a color to BRGA data.
        /// </summary>
        /// <param name="Color"></param>
        /// <param name="Bits"></param>
        public static void ColorToBGRA(Color Color, ref BGRADATA Bits)
        {
            ARGBDATA tibs = new ARGBDATA();

            ColorToARGB(Color, ref tibs);

            Bits.Alpha = tibs.Blue;
            Bits.Blue = tibs.Blue;
            Bits.Red = tibs.Red;
            Bits.Green = tibs.Green;
        }

        // Single Convert RGB-reversed to ColorRef

        /// <summary>
        /// Converts BRGA data to a color.
        /// </summary>
        /// <param name="Bits"></param>
        /// <returns></returns>
        public static Color BGRAToColor(BGRADATA Bits)
        {
            ARGBDATA tibs = new ARGBDATA();
            tibs.Alpha = Bits.Alpha;
            tibs.Red = Bits.Red;
            tibs.Blue = Bits.Blue;
            tibs.Green = Bits.Green;

            return ARGBToColor(tibs);
        }

        /// <summary>
        /// Converts BRG data to a color.
        /// </summary>
        /// <param name="Bits"></param>
        /// <returns></returns>
        public static Color BGRToColor(BGRDATA Bits)
        {
            RGBDATA tibs = new RGBDATA();
            tibs.Red = Bits.Red;
            tibs.Blue = Bits.Blue;
            tibs.Green = Bits.Green;

            return RGBToColor(tibs);
        }

        #region Multiple min and max

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static double Min(params double[] vars)
        {
            double d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return double.NaN;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static double Max(params double[] vars)
        {
            double d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return double.NaN;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static float Min(params float[] vars)
        {
            float d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return float.NaN;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static float Max(params float[] vars)
        {
            float d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return float.NaN;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static decimal Min(params decimal[] vars)
        {
            decimal d = default(decimal);
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return decimal.Zero;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static decimal Max(params decimal[] vars)
        {
            decimal d = default(decimal);
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return decimal.Zero;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static long Min(params long[] vars)
        {
            long d = 0;
            long i = 0;
            long c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static long Max(params long[] vars)
        {
            long d = 0;
            long i = 0;
            long c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static int Min(params int[] vars)
        {
            int d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static int Max(params int[] vars)
        {
            int d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static short Min(params short[] vars)
        {
            short d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static short Max(params short[] vars)
        {
            short d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static byte Min(params byte[] vars)
        {
            byte d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static byte Max(params byte[] vars)
        {
            byte d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static ulong Min(params ulong[] vars)
        {
            ulong d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static ulong Max(params ulong[] vars)
        {
            ulong d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static uint Min(params uint[] vars)
        {
            uint d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static uint Max(params uint[] vars)
        {
            uint d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static ushort Min(params ushort[] vars)
        {
            ushort d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static ushort Max(params ushort[] vars)
        {
            ushort d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the smallest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static sbyte Min(params sbyte[] vars)
        {
            sbyte d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Min(d, vars[i]);
            }

            return d;
        }

        /// <summary>
        /// Return the largest of an arbitrary number of numbers.
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        public static sbyte Max(params sbyte[] vars)
        {
            sbyte d = 0;
            int i = 0;
            int c = vars.Length - 1;

            if (c <= 0)
                return 0;

            d = vars[0];
            for (i = 1; i <= c; i++)
            {
                d = System.Math.Max(d, vars[i]);
            }

            return d;
        }

        #endregion Multiple min and max

        /// <summary>
        /// Converts a color to Hue, Saturation and Value.
        /// </summary>
        /// <param name="Color">The color to convert.</param>
        /// <param name="hsv">The HSV structure to fill.</param>
        public static void ColorToHSV(Color Color, ref HSVDATA hsv)
        {
            // http://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma
            // I adapted the current iteration of this equation from the one in Wikipedia.
            // I wish I could offer a better explanation.  But they'd do a better job.

            double hue = 0;
            double sat = 0;
            double val = 0;

            double Mn = 0;
            double Mx = 0;

            double r = 0;
            double g = 0;
            double b = 0;

            double chroma = 0;

            RGBDATA rgb = default(RGBDATA);

            ColorToRGB(Color, ref rgb);

            r = (double)(rgb.Red) / 255.0;
            g = (double)(rgb.Green) / 255.0;
            b = (double)(rgb.Blue) / 255.0;

            Mn = Min(r, g, b);
            Mx = Max(r, g, b);

            chroma = Mx - Mn;
            val = Mx;

            // actually, I figured this out in 1999, and they don't have a wiki on this.
            // sometimes the color doesn't exist... and sometimes it is more than 360...
            if ((Mn == Mx))
            {
                hsv.Hue = -1;

                val = Mn;
                            
                if (val <= 0.5)
                {
                    hsv.Saturation = 1;
                    hsv.Value = (510 * val) / 360;
                }
                else if (val <= 1)
                {
                    val = 1 - val;

                    hsv.Value = 1;
                    hsv.Saturation = (720 * val) / 360;
                }
                else
                {
                }

                return;
            }

            // this part is rewritten.
            if (Mx == r)
            {
                hue = ((g - b) / chroma) % 6;
            }
            else if (Mx == g)
            {
                hue = ((b - r) / chroma) + 2;
            }
            else if (Mx == b)
            {
                hue = ((r - g) / chroma) + 4;
            }

            hue *= 60;

            if ((hue < 0))
                hue = 360 + hue;

            sat = chroma / val;

            hsv.Value = val;
            hsv.Hue = hue;
            hsv.Saturation = sat;
        }

        /// <summary>
        /// Converts a Hue, Saturation and Value structure to a color.
        /// </summary>
        /// <param name="hsv">The HSV color structure to convert.</param>
        /// <returns>A Color structure.</returns>
        public static Color HSVToColor(HSVDATA hsv)
        {
            // http://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma
            // I adapted the current iteration of this equation from the one in Wikipedia.
            // I wish I could offer a better explanation.  But this isn't wikipedia, and they'd do a better job.
            //
            
            double a = 0;
            double b = 0;
            double c = 0;

            double chroma = 0;
            double Mx = 0;
            double Mn = 0;

            Color j = default(Color);

            double n = 0;


            if ((hsv.Hue >= 360))
                hsv.Hue -= 360;

            // actually, I figured this out in 1999, and they don't have a wiki on this.
            // sometimes the color doesn't exist... and sometimes it is more than 360...
            if ((hsv.Hue == -1))
            {
                var _with2 = hsv;
                if ((_with2.Saturation) > (_with2.Value))
                {
                    _with2.Saturation = 1;
                    n = ((_with2.Value * 360) / 510);
                }
                else
                {
                    _with2.Value = 255;
                    n = 1 - ((_with2.Saturation * 360) / 720);
                }

                a = n * 255;
                return Color.FromArgb(255, (int)a, (int)a, (int)a);
            }


            // this part is rewritten.
            chroma = hsv.Value * hsv.Saturation;

            Mn = System.Math.Abs(hsv.Value - chroma);
            Mx = hsv.Value;

            n = hsv.Hue / 60;

            a = Mx;
            c = Mn;

            b = chroma * (1 - System.Math.Abs((n % 2) - 1));

            b += c;

            // fit the color space in to byte space.
            a = System.Math.Round(a * 255);
            b = System.Math.Round(b * 255);
            c = System.Math.Round(c * 255);

            // Get the floored value of n
            n = System.Math.Floor(n);

            // this is another part that's been around since 1999.
            switch ((int)n)
            {
                case 0:
                case 6:
                    // 0, 360 - Red
                    j = Color.FromArgb(255, (int)a, (int)b, (int)c);

                    break;

                case 1:
                    // 60 - Yellow
                    j = Color.FromArgb(255, (int)b, (int)a, (int)c);

                    break;

                case 2:
                    // 120 - Green
                    j = Color.FromArgb(255, (int)c, (int)a, (int)b);

                    break;

                case 3:
                    // 180 - Cyan
                    j = Color.FromArgb(255, (int)c, (int)b, (int)a);

                    break;

                case 4:
                    // 240 - Blue
                    j = Color.FromArgb(255, (int)b, (int)c, (int)a);

                    break;

                case 5:
                    // 300 - Magenta
                    j = Color.FromArgb(255, (int)a, (int)c, (int)b);

                    break;
            }

            return j;
        }

        /// <summary>
        /// Converts a Hue, Saturation and Value structure to a raw 32-bit integer.
        /// </summary>
        /// <param name="hsv">The HSV color structure to convert.</param>
        /// <returns>A 32-bit ARGB color value.</returns>
        public static uint HSVToColorRaw(HSVDATA hsv)
        {
            // http://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma
            // I adapted the equation from the one in Wikipedia.
            // I wish I could offer a better explanation.  But this isn't wikipedia, and they'd do a better job.
            //

            uint a = 0;
            uint b = 0;
            uint c = 0;

            double chroma = 0;
            double Mx = 0;
            double Mn = 0;

            uint j = 0xff000000;

            double n = 0;

            if ((hsv.Hue >= 360))
                hsv.Hue -= 360;

            if ((hsv.Hue == -1))
            {
                if ((hsv.Saturation) > (hsv.Value))
                {
                    hsv.Saturation = 1;
                    n = ((hsv.Value * 360) / 510);
                }
                else
                {
                    hsv.Value = 255;
                    n = 1 - ((hsv.Saturation * 360) / 720);
                }

                a = (uint)(n * 255);
                return 0xff | (a << 16) | (a << 8) | (a);
            }

            chroma = hsv.Value * hsv.Saturation;

            Mn = System.Math.Abs(hsv.Value - chroma);
            Mx = hsv.Value;

            n = hsv.Hue / 60;

            a = (uint)Mx;
            c = (uint)Mn;

            b = (uint)(chroma * (1 - System.Math.Abs((n % 2) - 1)));

            b += c;

            // fit the color space in to byte space.
            a = (uint)System.Math.Round((double)a * 255);
            b = (uint)System.Math.Round((double)b * 255);
            c = (uint)System.Math.Round((double)c * 255);

            // Get the floored value of n
            n = System.Math.Floor(n);
            switch ((int)n)
            {
                case 0:
                case 6:
                    // 0, 360 - Red
                    j = j | (a << 16) | (b << 8) | c;

                    break;

                case 1:
                    // 60 - Yellow
                    j = j | (b << 16) | (a << 8) | c;

                    break;

                case 2:
                    // 120 - Green
                    j = j | (c << 16) | (a << 8) | b;

                    break;

                case 3:
                    // 180 - Cyan
                    j = j | (c << 16) | (b << 8) | a;

                    break;

                case 4:
                    // 240 - Blue
                    j = j | (b << 16) | (c << 8) | a;

                    break;

                case 5:
                    // 300 - Magenta
                    j = j | (a << 16) | (c << 8) | b;

                    break;
            }

            return j;
        }

        /// <summary>
        /// Converts a color to Cyan, Magenta, Yellow.
        /// </summary>
        /// <param name="Color">The color to convert.</param>
        /// <param name="cmy">The CMYDATA structure to fill.</param>
        public static void ColorToCMY(Color Color, ref CMYDATA cmy)
        {
            int r = 0;
            int g = 0;
            int b = 0;

            int x = 0;

            GetRGB(Color, ref r, ref g, ref b);

            x = Max(r, g, b);

            r = System.Math.Abs(1 - r);
            g = System.Math.Abs(1 - g);
            b = System.Math.Abs(1 - b);

            cmy.Magenta = (byte)r;
            cmy.Yellow = (byte)g;
            cmy.Cyan = (byte)b;
        }

        /// <summary>
        /// Converts a Cyan, Magenta, Yellow structure to a color.
        /// </summary>
        /// <param name="cmy">The CMYDATA structure to convert.</param>
        /// <returns>A Color structure.</returns>
        public static Color CMYToColor(CMYDATA cmy)
        {
            //
            int c = 0;
            int m = 0;
            int y = 0;

            int r = 0;
            int g = 0;
            int b = 0;

            c = cmy.Cyan;
            m = cmy.Magenta;
            y = cmy.Yellow;

            r = System.Math.Abs((1 - m));
            g = System.Math.Abs((1 - y));
            b = System.Math.Abs((1 - c));

            return Color.FromArgb(255, r, g, b);
        }

        /// <summary>
        /// Returns the relative percentages of each primary color in the specified color.
        /// </summary>
        /// <param name="Color">The color to calculate.</param>
        /// <param name="dpRed">The percentage of red as a value of 0.0 to 1.0</param>
        /// <param name="dpGreen">The percentage of green as a value of 0.0 to 1.0</param>
        /// <param name="dpBlue">The percentage of blue as a value of 0.0 to 1.0</param>
        public static void GetPercentages(Color Color, ref double dpRed, ref double dpGreen, ref double dpBlue)
        {
            double vR = 0;
            double vB = 0;
            double vG = 0;

            byte[] vIn = new byte[3];
            double d = 0;

            GetRGB(Color, ref vIn[0], ref vIn[1], ref vIn[2]);

            vR = (double)(vIn[0]);
            vG = (double)(vIn[1]);
            vB = (double)(vIn[2]);

            if ((vR > vG))
                d = vR;
            else
                d = vG;
            if ((vB > d))
                d = vB;

            if ((d == 0L))
                d = 255.0;

            dpRed = (vR / d);
            dpGreen = (vG / d);
            dpBlue = (vB / d);
        }

        /// <summary>
        /// Return the absolute color tone of the specified color.
        /// </summary>
        /// <param name="Color">The color to calculate.</param>
        /// <returns>An absolute gray tone color.</returns>
        public static Color AbsTone(Color Color)
        {
            double pR = 0;
            double pB = 0;
            double pG = 0;

            double sR = 0;
            double sB = 0;
            double sG = 0;

            GetPercentages(Color, ref pR, ref pG, ref pB);

            sR = (pR) * 255;
            sG = (pG) * 255;
            sB = (pB) * 255;

            return Color.FromArgb(Color.A, (int)(sR), (int)(sG), (int)(sB));
        }

        /// <summary>
        /// Sets the absolute tone of a color.
        /// </summary>
        /// <param name="rgbData">The RGB data to use.</param>
        /// <param name="pPercent">The percentage of gray to imbue.</param>
        /// <returns></returns>
        public static Color SetTone(ref RGBDATA rgbData, float pPercent)
        {
            return SetTone(ref rgbData, pPercent, Color.Transparent);
        }

        /// <summary>
        /// Sets the absolute tone of a color.
        /// </summary>
        /// <param name="rgbData">The RGB data to fill.</param>
        /// <param name="pPercent">The percentage of gray to imbue.</param>
        /// <param name="Color">The color to calculate.</param>
        /// <returns></returns>
        public static Color SetTone(ref RGBDATA rgbData, float pPercent, Color Color)
        {
            float x = 0;

            if ((Color != Color.Transparent)) ColorToRGB(Color, ref rgbData);
            x = Max(rgbData.Red, rgbData.Green, rgbData.Blue);

            if ((x == 0))
            {
                rgbData.Red = 0;
                rgbData.Green = 0;
                rgbData.Blue = 0;

                return Color.Empty;
            }

            rgbData.Red = (byte)(((float)(rgbData.Red) / x) * (255 * pPercent));
            rgbData.Green = (byte)(((float)(rgbData.Green) / x) * (255 * pPercent));
            rgbData.Blue = (byte)(((float)(rgbData.Blue) / x) * (255 * pPercent));

            return RGBToColor(rgbData);
        }

        /// <summary>
        /// Sets the absolute tone of a color.
        /// </summary>
        /// <param name="argbData">The ARGB data to use.</param>
        /// <param name="pPercent">The percentage of gray to imbue.</param>
        /// <returns></returns>
        public static Color SetTone(ref ARGBDATA argbData, float pPercent)
        {
            return SetTone(ref argbData, pPercent, Color.Transparent);
        }

        /// <summary>
        /// Sets the absolute tone of a color.
        /// </summary>
        /// <param name="argbData">The ARGB data to fill.</param>
        /// <param name="pPercent">The percentage of gray to imbue.</param>
        /// <param name="Color">The color to calculate.</param>
        /// <returns></returns>
        public static Color SetTone(ref ARGBDATA argbData, float pPercent, Color Color)
        {
            float x = 0;

            if ((Color != Color.Transparent)) ColorToARGB(Color, ref argbData);
            x = Max(argbData.Red, argbData.Green, argbData.Blue);

            if ((x == 0))
            {
                argbData.Red = 0;
                argbData.Green = 0;
                argbData.Blue = 0;

                return Color.Empty;
            }

            argbData.Red = (byte)(((float)(argbData.Red) / x) * (255 * pPercent));
            argbData.Green = (byte)(((float)(argbData.Green) / x) * (255 * pPercent));
            argbData.Blue = (byte)(((float)(argbData.Blue) / x) * (255 * pPercent));

            return ARGBToColor(argbData);
        }

        /// <summary>
        /// Gets the gray tone for the specified color.
        /// </summary>
        /// <param name="Color">The color to calculate.</param>
        /// <returns>A shade of gray.</returns>
        public static Color GrayTone(Color Color)
        {
            ARGBDATA rgbData = default(ARGBDATA);
            int a = 0;
            int b = 0;
            int c = 0;

            ColorToARGB(Color, ref rgbData);

            a = rgbData.Red;
            b = rgbData.Green;
            c = rgbData.Blue;

            a = (a + b + c) / 3;

            return Color.FromArgb(rgbData.Alpha, a, a, a);
        }

        /// <summary>
        /// Returns the average color between two colors.
        /// </summary>
        /// <param name="Color1">The first color to compare.</param>
        /// <param name="Color2">The second color to compare.</param>
        /// <returns>The average color.</returns>
        public static Color GetAverageColor(Color Color1, Color Color2)
        {
            RGBDATA[] Bits = new RGBDATA[3];
            float df = 0;
            int e = 0;

            float clr2 = (float)(Color2.ToArgb());

            byte al = (byte)(Color1.A);
            byte af = (byte)(Color2.A);

            ColorToRGB(Color1, ref Bits[0]);
            ColorToRGB(Color2, ref Bits[1]);

            e = 0;

            if ((System.Math.Round(clr2) != clr2))
            {
                df = ((float)(Bits[0].Red) * clr2);
                e = (int)(System.Math.Round(df, 0));
                Bits[2].Red = (byte)(e & 0xff);

                df = ((float)(Bits[0].Green) * clr2);
                e = (int)(System.Math.Round(df, 0));
                Bits[2].Green = (byte)(e & 0xff);

                df = ((float)(Bits[0].Blue) * clr2);
                e = (int)(System.Math.Round(df, 0));
                Bits[2].Blue = (byte)(e & 0xff);
            }
            else
            {
                df = ((float)(Bits[0].Red) + (float)(Bits[1].Red)) / 2L;
                e = (int)(System.Math.Round(df, 0));
                Bits[2].Red = (byte)(e & 0xff);

                df = ((float)(Bits[0].Green) + (float)(Bits[1].Green)) / 2L;
                e = (int)(System.Math.Round(df, 0));
                Bits[2].Green = (byte)(e & 0xff);

                df = ((float)(Bits[0].Blue) + (float)(Bits[1].Blue)) / 2L;
                e = (int)(System.Math.Round(df, 0));
                Bits[2].Blue = (byte)(e & 0xff);
            }

            // Get the average alpha
            al = (byte)(((int)(al) + (int)(af)) / 2);

            return Color.FromArgb(al, Bits[2].Red, Bits[2].Green, Bits[2].Blue);
        }

    }

}